# -*- coding: utf-8 -*-
import pygame
import random
import sys

# 游戏参数
GAME_NAME = 'TETRIS'
OFFSET = 50
CELL = 25
GAME_WIDTH = 10
GAME_HEIGHT = 20
SCREEN_WIDTH_LEFT = GAME_WIDTH * CELL
SCREEN_HEIGHT = GAME_HEIGHT * CELL
GAME_WIDTH_RIGHT = 6
SCREEN_WIDTH_RIGHT = GAME_WIDTH_RIGHT * CELL
SCREEN_SIZE_W = (GAME_WIDTH + GAME_WIDTH_RIGHT)* CELL + OFFSET * 3
SCREEN_SIZE_H = GAME_HEIGHT * CELL + OFFSET * 2
FPS = 25
FONT_NAME = 'arial'
FONT_SIZE = 20

class Piece(object):

    SHAPES = {
        'S': ((-1,0),(0,0),(0,-1),(1,-1)),
        'Z': ((-1,-1),(0,-1),(0,0),(1,0)),
        'J': ((-1,0),(0,0),(0,-1),(0,-2)),
        'L': ((0,-2),(0,-1),(0,0),(1,0)),
        'I': ((-1,0),(0,0),(1,0),(2,0)),
        'O': ((0,0),(0,1),(1,0),(1,1)),
        'T': ((-1,0),(0,0),(0,-1),(1,0)),
        }

    COLORS = [
        ((155,   0,   0), (175,  20,  20)),  # 红色
        ((  0, 155,   0), ( 20, 175,  20)),  # 绿色
        ((155, 155,   0), (175, 175,  20)),  # 黄色
        ((  0,   0, 155), ( 20,  20, 175)),  # 蓝色
        ]

    def __init__(self):
        self.pos = (4, -1)
        self.char = random.choice('SZJLIOT')
        self.data = self.SHAPES[self.char]
        self.color = random.choice(self.COLORS)

    def is_cell_ok(self, game, x, y):
        if y >= GAME_HEIGHT:
            return False
        if not 0 <= x < GAME_WIDTH:
            return False
        if (x, y) in game.map:
            return False
        return True

    def turn(self, game):
        if self.char == 'O':
            return
        data = map(lambda k: (-k[1], k[0]), self.data)
        for e in data:
            x, y = e[0] + self.pos[0], e[1] + self.pos[1]
            if not self.is_cell_ok(game, x, y):
                return
        self.data = data

    def move(self, game, dx, dy):
        if self.can_move(game, dx, dy):
            self.pos = self.pos[0] + dx, self.pos[1] + dy

    def can_move(self, game, dx, dy):
        for e in self.data:
            x, y = e[0] + self.pos[0] + dx, e[1] + self.pos[1] + dy
            if not self.is_cell_ok(game, x, y):
                return False
        return True


class Tetris(object):

    def __init__(self):
        self.game_name = GAME_NAME
        self.screen_size = ((SCREEN_SIZE_W, SCREEN_SIZE_H))
        self.fps = FPS
        self.font_name = FONT_NAME
        self.font_size = FONT_SIZE
        self.display_mode = pygame.HWSURFACE | pygame.DOUBLEBUF
        self.clock = pygame.time.Clock()

        pygame.init()
        pygame.mixer.init()
        pygame.display.set_caption(self.game_name)
        self.font = pygame.font.SysFont(self.font_name, self.font_size)
        self.background = pygame.display.set_mode(self.screen_size, self.display_mode)

        self.game_zone = pygame.Surface((SCREEN_WIDTH_LEFT, SCREEN_HEIGHT))
        self.game_info = pygame.Surface((SCREEN_WIDTH_RIGHT, SCREEN_HEIGHT))

        self.game_init()

    def game_init(self):        
        self.map = {}
        self.piece = Piece()
        self.next_piece = Piece()
        self.score = 0
        self.interval = 700

    def draw_cell(self, x, y, color, surface):
        pygame.draw.rect(surface, color[1], (x * CELL + 1, y * CELL + 1, CELL - 1, CELL - 1))
        pygame.draw.rect(surface, color[0], (x * CELL + 1, y * CELL + 1, CELL - 4, CELL - 4))

    def draw_next_piece(self):
        for x, y in self.next_piece.data:
            self.draw_cell(x + 2, y + 4 , self.next_piece.color, self.game_info)

    def draw_current_piece(self):
        for x, y in self.piece.data:
            self.draw_cell(x + self.piece.pos[0], y + self.piece.pos[1], self.piece.color, self.game_zone)

    def draw(self):
        self.game_zone.fill((0, 0, 0))
        self.game_info.fill((0, 0, 0))

        # 画已有格子
        for (x, y), value in self.map.iteritems():
            self.draw_cell(x, y, value, self.game_zone)

        # 画正在下落的格子
        self.draw_current_piece()

        # 画next下落的格子
        out = self.font.render('NEXT', True, (255, 255, 255))
        self.game_info.blit(out, (10, 10))
        self.draw_next_piece()

        # 分数
        out = self.font.render('SCORE', True, (255, 255, 255))
        self.game_info.blit(out, (10, 200))
        out = self.font.render('%u' % self.score, True, (255, 255, 255))
        self.game_info.blit(out, (20, 230))
        

        pygame.draw.rect(self.background, (255, 255, 255), (OFFSET - 2, OFFSET - 2, SCREEN_WIDTH_LEFT + 4, SCREEN_HEIGHT + 4), 2)
        pygame.draw.rect(self.background, (255, 255, 255), (SCREEN_WIDTH_LEFT + 2 * OFFSET - 2, OFFSET - 2, SCREEN_WIDTH_RIGHT + 4, SCREEN_HEIGHT + 4), 2)
        self.background.blit(self.game_zone, (OFFSET, OFFSET))
        self.background.blit(self.game_info, (SCREEN_WIDTH_LEFT + 2 * OFFSET, OFFSET))

    def quit(self):        
        pygame.quit()
        sys.exit(0)

    def merge_picec(self):
        for e in self.piece.data:
            x, y = e[0] + self.piece.pos[0], e[1] + self.piece.pos[1]
            self.map[(x, y)] = self.piece.color

    def fall2bottom(self):
        while self.piece.can_move(self, 0, 1):
             self.piece.move(self, 0, 1) 

    def is_line_full(self, line):
        for x in range(0, GAME_WIDTH):
            if (x, line) not in self.map:
                return False
        return True

    def cancel_oneline(self, line):
        for x in range(0, GAME_WIDTH):
            self.map.pop((x, line))
        for y in range(line - 1, -1, -1):
            for x in range(0, GAME_WIDTH):
                if (x, y) in self.map: 
                    self.map[(x, y + 1)] = self.map.pop((x, y))

    def cancel_all(self):
        count = 0
        for y in range(0, GAME_HEIGHT):
            if self.is_line_full(y):
                count += 1
                self.cancel_oneline(y)
        return count

    def is_gameove(self):
        for x in range(0, GAME_WIDTH):
            if (x, -1) in self.map:
                return True
        return False

    def restart(self):
        self.game_init()

    def run(self):
        last_down_time = pygame.time.get_ticks()
        while True:
            # 判断是否挂了
            if self.is_gameove():
                last_down_time = pygame.time.get_ticks()
            
            # 用户按键处理
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.quit()
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        self.quit()
                    elif event.key == pygame.K_r:
                        self.restart()
                    elif event.key == pygame.K_LEFT:                        
                        self.piece.move(self, -1, 0)
                    elif event.key == pygame.K_RIGHT:
                        self.piece.move(self, 1, 0)
                    elif event.key == pygame.K_DOWN:
                        self.piece.move(self, 0, 1)
                    elif event.key == pygame.K_UP:
                        self.piece.turn(self)
                    elif event.key == pygame.K_SPACE:
                        self.fall2bottom()

            if pygame.time.get_ticks() - last_down_time > self.interval:
                # 下落处理
                if self.piece.can_move(self, 0, 1):
                    self.piece.move(self, 0, 1)
                else:
                    self.merge_picec()
                    count = self.cancel_all()
                    add_score = (2 ** count - 1) * 10
                    self.score += add_score
                    self.interval = max(200, self.interval - add_score)
                    self.piece = self.next_piece
                    self.next_piece = Piece() 
                last_down_time = pygame.time.get_ticks()

            # 绘图
            self.draw()

            pygame.display.flip()
            self.clock.tick(self.fps)

if __name__ == '__main__':
    game = Tetris()
    game.run()
